package lol.clann.bukkit.pluginboot.util;

import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import lol.clann.bukkit.pluginboot.PluginBoot;
import lol.clann.bukkit.pluginboot.version.VersionSetup;
import lol.clann.clannboot.annotation.inject.Inject;
import lol.clann.clannboot.annotation.load.Load;
import lol.clann.clannboot.annotation.svc.PostInstance;
import lol.clann.clannboot.annotation.svc.Service;
import lol.clann.clannboot.util.clazz.*;
import org.apache.commons.io.IOUtils;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.nutz.lang.Lang;
import org.slf4j.Logger;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.zip.ZipFile;

/**
 * @author pyz
 * @date 2018/8/8 下午10:36
 */
@Service(priority = Integer.MAX_VALUE)
public class TranslateUtils {

    public static final WrapClass classLocaleLanguage = ClassUtils.forName("{nms}.LocaleLanguage");

    private static TranslateUtils self;

    @Inject
    private Logger logger;
    @Inject
    private PluginBoot plugin;

    private Map<String, String> lang = new HashMap<>();
    @Inject
    private ItemStackUtils itemStackUtils;

    private WrapClass classLanguageRegistry;
    private WrapField LanguageRegistryInstance;
    private WrapField modLanguageData;
    private WrapField instance;
    private WrapField languageList;

    private TranslateUtils() {
        if (self != null) {
            throw new RuntimeException();
        }
        self = this;
    }

    public static TranslateUtils getInstance() {
        return self;
    }

    @PostInstance
    private void postInstance() {
        instance = classLocaleLanguage.getField(FieldFilter.build().type(classLocaleLanguage).static_(true));
        languageList = classLocaleLanguage.getField(FieldFilter.build().type(Map.class).static_(false).final_(true));
        if (VersionSetup.isForge()) {
            classLanguageRegistry = ClassUtils.forName("cpw.mods.fml.common.registry.LanguageRegistry");
            LanguageRegistryInstance = classLanguageRegistry.getField(FieldFilter.build().type(classLanguageRegistry));
            modLanguageData = classLanguageRegistry.getField(FieldFilter.build().type(Map.class));
        }
    }

    @Load
    private void init() throws Exception {
        logger.info("语言模块加载成功\n本插件配置文件目录下,lang文件夹中的语言文件将自动扫描载入");
        init0();
        logger.info("语言映射建立完毕");
    }

    public Map<String, String> getLang() {
        return lang;
    }

    public String tryTranslateKey(String key) {
        return lang.getOrDefault(key, key);
    }

    public String getTranslationValue(Object o) {
        if (o == null) {
            return null;
        }
        if (o instanceof ItemStack) {
            return itemStackUtils.getDisplayName((ItemStack) o);
        }
        throw new RuntimeException("不支持的类型:" + o.getClass().getName());
    }

    private void init0() throws Exception {
        Object localeLanguage = instance.get(null);
        Map languageListMap = (Map) languageList.get(localeLanguage);

        Properties en_us = new Properties();
        Properties zh_cn = new Properties();

        if (VersionSetup.isForge()) {
            Map<String, Properties> modLanguageData = (Map<String, Properties>) this.modLanguageData.get(LanguageRegistryInstance.get(null));
            modLanguageData.forEach((k, v) -> {
                if (k.toLowerCase().equals("en_us")) {
                    en_us.putAll(v);
                } else if (k.toLowerCase().equals("zh_cn")) {
                    zh_cn.putAll(v);
                }
            });
            languageListMap.putAll(en_us);
            languageListMap.putAll(zh_cn);

            //加载中文语言映射
            //从Classloader读取jar包时一定要注意系统库,不要读他
            List<URL> urls = Invoker.of(ClassUtils.forName("net.minecraft.launchwrapper.Launch").getHandle())
                    .get("classLoader")
                    .invoke("getSources")
                    .get();
            //所有插件
            for (Plugin plugin1 : Bukkit.getPluginManager().getPlugins()) {
                urls.addAll(Arrays.asList(((URLClassLoader) plugin1.getClass().getClassLoader()).getURLs()));
            }
            urls.stream()
                    .flatMap(url -> open(url))//展开文件夹
                    .filter(url -> url.toString().endsWith(".jar"))
                    .forEach(url -> {
                        try {
                            if (!Files.isReadable(Paths.get(url.toURI()))) {
                                logger.warn("语言映射扫描,无读取权限:" + url.toString());
                                return;
                            }
                            ZipFile zipFile = new ZipFile(Paths.get(url.toURI()).toFile());
                            zipFile.stream().filter(en -> en.getName().toLowerCase().endsWith("zh_cn.lang"))
                                    .forEach(en -> {
                                        try (InputStream in = zipFile.getInputStream(en)) {
                                            Map<String, String> map = parseLangFile(in);
                                            languageListMap.putAll(map);
                                            logger.info("注入语言文件:{}", en.toString());
                                        } catch (IOException e) {
                                            throw Lang.wrapThrow(e);
                                        }
                                    });
                        } catch (Throwable e) {
                            logger.warn("jar包扫描失败,jar=" + url.toString(), e.getMessage());
                        }
                    });
        }

        //加载自定义语言映射
        File dir = new File(plugin.getDataFolder(), "lang");
        if (!dir.exists()) {
            return;
        }
        for (File file : dir.listFiles()) {
            if (file.getName().endsWith(".lang")) {
                try (InputStream is = new FileInputStream(file)) {
                    Map<String, String> map = parseLangFile(is);
                    languageListMap.putAll(map);
                    logger.info("注入语言文件:{}", file.getName());
                }
            }
        }
    }

    /**
     * 若url是文件,展开之
     *
     * @param url
     * @return
     */
    private Stream<URL> open(URL url) {
        String protocol = url.getProtocol().toLowerCase();
        try {
            switch (protocol) {
                case "file":
                    return open(Paths.get(url.toURI()));
                case "asmgen":
                    return Stream.empty();
                default:
                    throw new RuntimeException("非法URL协议:" + url.toString());
            }
        } catch (Exception e) {
            throw Lang.wrapThrow(e);
        }
    }

    private Stream<URL> open(Path file) {
        try {
            if (Files.isDirectory(file)) {
                return Files.walk(file, Integer.MAX_VALUE)
                        .filter(f -> !Files.isDirectory(f))
                        .map(f -> {
                            try {
                                return f.toUri().toURL();
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        });
            } else {
                return Stream.of(file.toUri().toURL());
            }
        } catch (Exception e) {
            throw Lang.wrapThrow(e);
        }
    }

    private Map<String, String> parseLangFile(InputStream inputStream) {
        Map<String, String> map = new HashMap<>();
        try {
            Splitter b = Splitter.on('=').limit(2);
            Pattern a = Pattern.compile("%(\\d+\\$)?[\\d\\.]*[df]");
            Iterator var2 = IOUtils.readLines(inputStream, StandardCharsets.UTF_8).iterator();
            while (var2.hasNext()) {
                String var3 = (String) var2.next();
                if (!var3.isEmpty() && var3.charAt(0) != '#') {
                    String[] var4 = (String[]) Iterables.toArray(b.split(var3), String.class);
                    if (var4 != null && var4.length == 2) {
                        String var5 = var4[0];
                        String var6 = a.matcher(var4[1]).replaceAll("%$1s");
                        map.put(var5, var6);
                    }
                }
            }
        } catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        return map;
    }

}
